/**
 *博尔信基础开发框架
 *<br> 版权声明：厦门博尔信软件有限公司版权所有,违者必究
 *<br> Copyright:  Copyright (c) 2012
 *<br> Company:pearlsen
 *<br> @author 蒋勇华
 *<br> 2012-3-26
 *<br> @version 1.0
 *————————————————————————————————————
 *修改记录
 *    修改者：
 *    修改时间：
 *    修改原因：
 *——————————————————————————————————————
 */
package com.pearlsoft.util;

import java.beans.PropertyDescriptor;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.beanutils.NestedNullException;
import org.apache.commons.beanutils.PropertyUtils;

import com.pearlsoft.platform.core.exception.BeanAccessException;

/**
 * 描述:
 *
 * @author 蒋勇华
 */
public class BeanUtils {
	public static Object getStaticProperty(Class clazz, String name) {
		try {
			return clazz.getField(name).get(null);
		} catch (IllegalArgumentException e) {
			throw new BeanAccessException("在读取类[" + clazz + "]的静态属性[" + name
					+ "]时遇到错误", e);
		} catch (SecurityException e) {
			throw new BeanAccessException("在读取类[" + clazz + "]的静态属性[" + name
					+ "]时遇到错误", e);
		} catch (IllegalAccessException e) {
			throw new BeanAccessException("在读取类[" + clazz + "]的静态属性[" + name
					+ "]时遇到错误", e);
		} catch (NoSuchFieldException e) {
			throw new BeanAccessException("在读取类[" + clazz + "]的静态属性[" + name
					+ "]时遇到错误", e);
		}
	}

	public static Object getProperty(Object bean, String name) {
		try {
			return PropertyUtils.getProperty(bean, name);
		} catch (NestedNullException e) {
			return null;
		} catch (IllegalAccessException e) {
			throw new BeanAccessException("在读取bean[" + bean + "]的属性[" + name
					+ "]时遇到错误", e);
		} catch (InvocationTargetException e) {
			throw new BeanAccessException("在读取bean[" + bean + "]的属性[" + name
					+ "]时遇到错误", e);
		} catch (NoSuchMethodException e) {
			throw new BeanAccessException("在读取bean[" + bean + "]的属性[" + name
					+ "]时遇到错误", e);
		}
	}

	public static void setProperty(Object bean, String name, Object value) {
		try {
			PropertyUtils.setProperty(bean, name, value);
		} catch (IllegalAccessException e) {
			throw new BeanAccessException("在将属性[" + name + "]写入bean[" + bean
					+ "]时遇到错误", e);
		} catch (InvocationTargetException e) {
			throw new BeanAccessException("在将属性[" + name + "]写入bean[" + bean
					+ "]时遇到错误", e);
		} catch (NoSuchMethodException e) {
			throw new BeanAccessException("在将属性[" + name + "]写入bean[" + bean
					+ "]时遇到错误", e);
		}
	}

	public static void setSimpleProperty(Object bean, String name, Object value) {
		try {
			PropertyUtils.setSimpleProperty(bean, name, value);
		} catch (IllegalAccessException e) {
			throw new BeanAccessException("在将属性[" + name + "]写入bean[" + bean
					+ "]时遇到错误", e);
		} catch (InvocationTargetException e) {
			throw new BeanAccessException("在将属性[" + name + "]写入bean[" + bean
					+ "]时遇到错误", e);
		} catch (NoSuchMethodException e) {
			throw new BeanAccessException("在将属性[" + name + "]写入bean[" + bean
					+ "]时遇到错误", e);
		}
	}

	/**
	 * 获取bean的指定名称的属性，如果不存在指定的属性，返回null
	 * 
	 * @param bean
	 * @param name
	 * @return
	 */
	public static Object getPropertyIf(Object bean, String name) {
		if (hasProperty(bean, name)) {
			return getProperty(bean, name);
		} else
			return null;
	}

	public static Object getSimpleProperty(Object bean, String name) {
		try {
			return PropertyUtils.getSimpleProperty(bean, name);
		} catch (IllegalAccessException e) {
			throw new BeanAccessException("在读取bean[" + bean + "]的属性[" + name
					+ "]时遇到错误", e);
		} catch (InvocationTargetException e) {
			throw new BeanAccessException("在读取bean[" + bean + "]的属性[" + name
					+ "]时遇到错误", e);
		} catch (NoSuchMethodException e) {
			throw new BeanAccessException("在读取bean[" + bean + "]的属性[" + name
					+ "]时遇到错误", e);
		}
	}

	/**
	 * 返回bean是否有指定的属性
	 * 
	 * @param bean
	 * @param name
	 * @return
	 */
	public static boolean hasProperty(Object bean, String name) {
		if (bean == null || StringUtils.isBlank(name))
			return false;
		return getPropertyDescriptor(bean.getClass(), name) != null;
	}

	/**
	 * 调用指定的方法
	 * 
	 * @param object
	 * @param methodName
	 * @param args
	 *            方法参数，不能用null值
	 * @return
	 */
	public static Object invokeMethod(Object object, String methodName,
			Object[] args, Class[] parameterTypes) {
		try {
			// 试着将参数转换成合法的参数类型
			if (args != null && args.length > 0 && parameterTypes != null
					&& parameterTypes.length > 0
					&& args.length == parameterTypes.length) {
				Object[] args2 = new Object[args.length];
				for (int i = 0; i < args.length; i++) {
					args2[i] = DataUtils.as(args[i], parameterTypes[i]);
				}
				args = args2;
			}
			return MethodUtils.invokeMethod(object, methodName, args,
					parameterTypes);
		} catch (NoSuchMethodException e) {
			throw new BeanAccessException("调用bean[" + object + "的方法["
					+ methodName + "]时遇到一个错误", e);
		} catch (IllegalAccessException e) {
			throw new BeanAccessException("调用bean[" + object + "的方法["
					+ methodName + "]时遇到一个错误", e);
		} catch (InvocationTargetException e) {
			throw new BeanAccessException("调用bean[" + object + "的方法["
					+ methodName + "]时遇到一个错误", e);
		}
	}

	public static PropertyDescriptor getPropertyDescriptor(Class beanClass,
			String proName) {
		return org.springframework.beans.BeanUtils.getPropertyDescriptor(
				beanClass, proName);
	}

	public static PropertyDescriptor[] getPropertyDescriptors(Class beanClass) {
		return org.springframework.beans.BeanUtils
				.getPropertyDescriptors(beanClass);
	}

	/**
	 * 返回一个bean的Map形式
	 * 
	 * @param bean
	 * @param ignoreProperties
	 *            要忽略的属性名
	 * @return
	 */
	public static Map<String, Object> describe(Object bean,
			String[] ignoreProperties) {
		if (bean == null)
			return null;
		if (bean instanceof DynaBean) {
			try {
				return BeanUtilsBean.getInstance().describe(bean);
			} catch (IllegalAccessException e) {
				throw new BeanAccessException("在将Bean[" + bean
						+ "]转换成Map形式时遇到一个错误:", e);
			} catch (InvocationTargetException e) {
				throw new BeanAccessException("在将Bean[" + bean
						+ "]转换成Map形式时遇到一个错误:", e);
			} catch (NoSuchMethodException e) {
				throw new BeanAccessException("在将Bean[" + bean
						+ "]转换成Map形式时遇到一个错误:", e);
			}
		}

		Map<String, Object> description = new HashMap<String, Object>();
		PropertyDescriptor[] descriptors = BeanUtilsBean.getInstance()
				.getPropertyUtils().getPropertyDescriptors(bean);
		Class clazz = bean.getClass();
		for (int i = 0; i < descriptors.length; i++) {
			String name = descriptors[i].getName();
			boolean ignore = false;
			if (ignoreProperties != null) {
				for (String ignorePro : ignoreProperties) {
					if (name.equals(ignorePro)) {
						ignore = true;
						break;
					}
				}
			}
			if (ignore)
				continue;
			if (MethodUtils.getAccessibleMethod(clazz, descriptors[i]
					.getReadMethod()) != null) {
				description.put(name, getSimpleProperty(bean, name));
			}
		}
		return description;

	}

	/**
	 * 返回一个bean的Map形式
	 * 
	 * @param bean
	 * @return
	 */
	public static Map<String, Object> describe(Object bean) {
		return describe(bean, null);
	}

	public static void printBean(PrintStream ps, Object bean,
			String... ignoreProperties) {
		ps.println("start printBean:" + bean.getClass());
		if (bean == null) {
			ps.println("null");
			return;
		}
		Map<String, Object> map = describe(bean, ignoreProperties);
		for (Map.Entry<String, Object> ent : map.entrySet()) {
			ps.println(ent.getKey() + ": " + ent.getValue());
		}
		ps.println("end printBean:" + bean.getClass());
	}

	public static void printBean(Object bean, String... ignoreProperties) {
		printBean(System.out, bean, ignoreProperties);
	}

	/**
	 * 打印两个bean不同的属性
	 * 
	 * @param ps
	 * @param bean1
	 * @param bean2
	 * @param ignoreProperties
	 */
	public static void printCompareBeans(PrintStream ps, Object bean1,
			Object bean2, String... ignoreProperties) {
		ps
				.println("start compare " + bean1.getClass() + ","
						+ bean2.getClass());
		Map<String, Object> map1 = describe(bean1, ignoreProperties);
		Map<String, Object> map2 = describe(bean2, ignoreProperties);
		Set<String> keySet1 = map1.keySet();
		Set<String> keySet2 = map2.keySet();
		for (String key : keySet1) {
			Object value1 = map1.get(key);
			Object value2 = map2.get(key);
			if (!equals(value1, value2)) {
				ps.println("key=" + key);
				ps.println("value1=" + value1);
				ps.println("value2=" + value2);
			}
			keySet2.remove(key);
		}
		for (String key : keySet2) {
			Object value1 = map1.get(key);
			Object value2 = map2.get(key);
			if (!equals(value1, value2)) {
				ps.println("key=" + key);
				ps.println("value1=" + value1);
				ps.println("value2=" + value2);
			}
		}
		ps.println("end compare " + bean1.getClass() + "," + bean2.getClass());
	}

	/**
	 * 打印两个bean不同的属性
	 * 
	 * @param bean1
	 * @param bean2
	 * @param ignoreProperties
	 */
	public static void printCompareBeans(Object bean1, Object bean2,
			String... ignoreProperties) {
		printCompareBeans(System.out, bean1, bean2, ignoreProperties);
	}

	public static boolean equals(Object obj1, Object obj2) {
		if (obj1 == null) {
			return obj2 == null;
		}
		return obj1.equals(obj2);
	}

	/**
	 * 返回类对于指定超类或接口实现的类型参数数组
	 * 
	 * <pre>
	 * 例如有如下定义：
	 * class A<T>{} 
	 * 
	 * interface IA<T1,T2>{} 
	 * 
	 * class B extends A<String> implements IA<Double,Integer>{}
	 * 
	 * class C<T1,T2> extends A<T1> implements IA<T2,Integer>{}
	 * 
	 * class C2 extends C<Double,String>{}
	 * 
	 * class D extends C{}
	 * 
	 * getTypeArguments(B.class,A.class) 返回[String.class]
	 * getTypeArguments(B.class,IA.class) 返回[Double.class,Integer.class]
	 * getTypeArguments(C2.class,A.class) 返回[Double.class]
	 * getTypeArguments(C2.class,IA.class) 返回[String.class,Integer.class]
	 * getTypeArguments(C.class,IA.class) 返回[null,Integer.class]
	 * getTypeArguments(D.class,C.class) 返回null
	 * </pre>
	 * 
	 * @param cls
	 * @param superType
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static Type[] getTypeArguments(Class cls, Class superType) {
		if (superType.isAssignableFrom(cls))
			return searchTypeArguments(cls, cls, superType);
		else
			return null;
	}

	@SuppressWarnings("unchecked")
	private static Type[] searchTypeArguments(Class rootCls, Class cls,
			Class superType) {
		Type[] sts = getAllGenericSuperTypes(cls);
		for (Type st : sts) {
			if (superType == st) {

			} else if (st instanceof ParameterizedType
					&& ((ParameterizedType) st).getRawType() == superType) {
				Type[] rs = ((ParameterizedType) st).getActualTypeArguments();
				for (int i = 0; i < rs.length; i++) {
					if (rs[i] instanceof TypeVariable) {
						rs[i] = getTypeArgumentsForTypeVariable(rootCls, cls,
								(TypeVariable) rs[i]);
					}
				}
				return rs;
			}
		}
		for (Type st : sts) {
			Type[] rs = null;
			if (st instanceof Class) {
				rs = searchTypeArguments(rootCls, (Class) st, superType);
			} else if (st instanceof ParameterizedType) {
				rs = searchTypeArguments(rootCls,
						(Class) (((ParameterizedType) st).getRawType()),
						superType);
			}
			if (rs != null)
				return rs;
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	private static Type getTypeArgumentsForTypeVariable(Class cls,
			Class superType, TypeVariable typeVariable) {
		int k = -1;
		for (int i = 0; i < superType.getTypeParameters().length; i++) {
			if (typeVariable == superType.getTypeParameters()[i]) {
				k = i;
				break;
			}
		}
		if (k >= superType.getTypeParameters().length || k < 0) {
			throw new RuntimeException(typeVariable + "不是" + superType
					+ "的类型参数变量");
		}
		return searchTypeArgumentsForTypeVariable(cls, cls, superType, k);
	}

	@SuppressWarnings("unchecked")
	private static Type searchTypeArgumentsForTypeVariable(Class rootCls,
			Class cls, Class superType, int i) {
		Type[] sts = getAllGenericSuperTypes(cls);
		for (Type st : sts) {
			if (superType == st) {

			} else if (st instanceof ParameterizedType
					&& ((ParameterizedType) st).getRawType() == superType) {
				Type t = ((ParameterizedType) st).getActualTypeArguments()[i];
				if (t instanceof TypeVariable) {
					Type t2 = getTypeArgumentsForTypeVariable(rootCls, cls,
							(TypeVariable) t);
					if (t2 != null)
						t = t2;
				}
				return t;
			}
		}
		for (Type st : sts) {
			Type r = null;
			if (st instanceof Class) {
				r = searchTypeArgumentsForTypeVariable(rootCls, (Class) st,
						superType, i);
			} else if (st instanceof ParameterizedType) {
				r = searchTypeArgumentsForTypeVariable(rootCls,
						(Class) (((ParameterizedType) st).getRawType()),
						superType, i);
			}
			if (r != null)
				return r;
		}
		return null;
	}

	private static Type[] getAllGenericSuperTypes(Class cls) {
		Type[] superTypes;
		Type[] ts = cls.getGenericInterfaces();
		if (ts != null && ts.length > 0) {
			superTypes = new Type[ts.length + 1];
			System.arraycopy(ts, 0, superTypes, 1, ts.length);
		} else {
			superTypes = new Type[1];
		}
		superTypes[0] = cls.getGenericSuperclass();
		return superTypes;
	}

	public static boolean isSimpleValueType(Class clazz) {
		return org.springframework.beans.BeanUtils.isSimpleValueType(clazz);
	}

}
